﻿using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Godot.core
{
    using SysType = System.Type;
    using SysMethodInfo = System.Reflection.MethodInfo;

    public struct MethodFlags
    {
        public const uint METHOD_FLAG_NORMAL = 1;
	    public const uint METHOD_FLAG_EDITOR=2;
	    public const uint METHOD_FLAG_NOSCRIPT=4;
	    public const uint METHOD_FLAG_CONST=8;
	    public const uint METHOD_FLAG_REVERSE=16; // used for events
	    public const uint METHOD_FLAG_VIRTUAL=32;
        public const uint METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL;
    }

    public abstract class MethodBind
    {
        protected int _method_id;
        protected uint _hint_flags;
        protected StringName _name;
        protected List<Variant> _default_arguments;
        protected int _argument_count;
        protected bool _const;

        public StringName name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
            }
        }

        protected bool is_const 
        {
            get { return _const; }
        }

        public int argument_count
        {
            get { return _argument_count; }
        }

        public IList<Variant> default_arguments
        {
            get
            {
                return _default_arguments.AsReadOnly();
            }
            set
            {
                _default_arguments.Clear();
                _default_arguments.AddRange(value);
            }
        }

        public uint hint_flags
        {
            get
            {
                return hint_flags | (_const ? MethodFlags.METHOD_FLAG_CONST : 0);
            }
            set
            {
                hint_flags = value;
            }
        }

        public bool has_default_argument(int p_arg)
        {
            int idx = _argument_count - p_arg - 1;

            if (idx < 0 || idx >= _default_arguments.Count)
                return false;
            else
                return true;
        }

        public abstract string get_instance_type();
        public abstract Variant call(Object p_object, Variant[] p_args, ref Variant.CallError r_error);

        public static MethodBind create_method_bind(SysMethodInfo p_method)
        {
            return null;
        }

        public static MethodBind create_method_bind(System.Delegate p_method)
        {
            return null;
        }

        public static MethodBind create_native_method_bind<T>(SysMethodInfo p_method, MethodInfo p_info ) 
        {
	        MethodBindNative<T>  a = new MethodBindNative<T>();
	        a.method = p_method;
	        a.method_info = p_info;
	        return a;
        }
    }
}
